﻿using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Windows.Forms;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using WeifenLuo.WinFormsUI.Docking;
using ScintillaNet;
using T3K.SketchEditor.Core;

namespace T3K.SketchEditor.Forms {
	public partial class DocumentForm : DockContent {

		#region Fields

		private string _filePath;
        string[] slipSeparator = new string[] { ".", "::", "->" };
        T3K.SketchEditor.Core.EditorManager _editor = T3K.SketchEditor.Core.EditorManager.Instance;

		#endregion Fields

		#region Properties

		public string FilePath
		{
			get { return _filePath; }
			set { _filePath = value; }
		}


		public Scintilla Scintilla
		{
			get
			{
				return scintilla;
			}
		}

		#endregion Properties

		#region Constructors

		public DocumentForm()
		{
			InitializeComponent();
            scintilla.KeyUp += new KeyEventHandler(scintilla_KeyUp);
            scintilla.PreviewKeyDown += new PreviewKeyDownEventHandler(scintilla_PreviewKeyDown);

			scintilla.ConfigurationManager.Language = "cs";
            scintilla.Indentation.SmartIndentType = SmartIndent.CPP;

            scintilla.AutoComplete.DropRestOfWord = true;
            scintilla.AutoComplete.SingleLineAccept = false;


            foreach (TagCollection.Tag tag in TagCollection.Instance.Tags)
                autoCompleteList.Add(tag.Keyword);

		}

        void UpdateAutoCompleteList() {

            cAutoCompleteList.Clear();
            tmpAutoCompleteList.Clear();
            tmp2AutoCompleteList.Clear();
            scintilla.AutoComplete.SingleLineAccept = false;

            tmp2AutoCompleteList.AddRange(autoCompleteList);

            Regex r = new Regex(@"([a-zA-Z_][a-zA-Z0-9_\\.:]*)\s([a-zA-Z_][a-zA-Z0-9_]*)[\s;=\\(]");
            Match m = r.Match(scintilla.Text);
            Dictionary<string, string> vars = new Dictionary<string, string>();

            while (m != null && m.Success) {
                string type = m.Groups[1].Value;
                string var = m.Groups[2].Value;

                if (!vars.ContainsKey(var))
                    vars.Add(var, type);
                else
                    vars[var] = type;

                m = m.NextMatch();
            }

            r = new Regex(@"([a-zA-Z_][a-zA-Z0-9_]*)::([a-zA-Z_][a-zA-Z0-9_]*)[\s;=\\(]");
            m = r.Match(scintilla.Text);

            while (m != null && m.Success) {
                string a = m.Groups[1].Value;
                string b = m.Groups[2].Value;

                string entry = string.Format("{0}.{1}", a, b);

                if (!tmp2AutoCompleteList.Contains(entry))
                    tmp2AutoCompleteList.Add(entry);

                m = m.NextMatch();
            }

            tmpAutoCompleteList.AddRange(tmp2AutoCompleteList);

            foreach (string var in vars.Keys) {

                string[] varTokens = vars[var].Split(slipSeparator, StringSplitOptions.None);
                

                for (int i = 0; i < tmp2AutoCompleteList.Count; i++) {
                    string[] autoCompleteTokens = tmp2AutoCompleteList[i].Split(slipSeparator, StringSplitOptions.None);
                    if (varTokens.Length <= autoCompleteTokens.Length) {


                        bool valid = true;
                        int tokenPos = 0;
                        for (int t = 0; t < varTokens.Length; t++) {
                            if (autoCompleteTokens[t].StartsWith(varTokens[t]))
                                tokenPos = t;
                            else {
                                valid = false;
                                break;
                            }
                        }

                        if (valid) {

                            int lengthDiff = autoCompleteTokens.Length - varTokens.Length + 1;
                            string[] newAutoCEntry = new string[lengthDiff];
                            newAutoCEntry[0] = var;

                            for (int t = varTokens.Length, nt = 1; t < autoCompleteTokens.Length; t++, nt++) {
                                newAutoCEntry[nt] = autoCompleteTokens[t];
                            }

                            string entry = string.Join(".", newAutoCEntry);
                            if (!tmpAutoCompleteList.Contains(entry))
                                tmpAutoCompleteList.Add(entry);
                        }

                    }
                }
            }            

            System.Text.StringBuilder currentWord = new System.Text.StringBuilder();
            System.Collections.Generic.Stack<char> prefix = new System.Collections.Generic.Stack<char>();
            System.Collections.Generic.List<char> sufix = new System.Collections.Generic.List<char>();

            char[] startIgl = new char[] { '(', '[' };
            char[] endIgl = new char[] { ')', ']' };
            int regionCount = 0;

            for (int i = scintilla.Selection.Start - 1; i >= 0 && i < scintilla.Text.Length; i--) {
                char cc = scintilla.Text[i];

                if (Utils.Check(cc, endIgl)) {
                    regionCount++;
                } else if (Utils.Check(cc, startIgl)) {
                    if (regionCount > 0)
                        regionCount--;
                    else
                        break;
                } else if (char.IsWhiteSpace(cc))
                    break;
                else {
                    if (regionCount == 0)
                        prefix.Push(cc);
                }
            }


            for (int i = scintilla.Selection.Start; i < scintilla.Text.Length; i++) {
                char cc = scintilla.Text[i];
                if (char.IsWhiteSpace(cc))
                    break;
                else {
                    sufix.Add(cc);
                }
            }

            currentWord.Append(prefix.ToArray());
            currentWord.Append(sufix.ToArray());

            

            string[] currentWordTokens = currentWord.ToString().Split(slipSeparator, StringSplitOptions.None);

            for (int i = 0; i < tmpAutoCompleteList.Count; i++) {

                string[] autoCompleteTokens = tmpAutoCompleteList[i].Split(slipSeparator, StringSplitOptions.None);
                if (currentWordTokens.Length <= autoCompleteTokens.Length) {
                    bool valid = true;
                    int tokenPos = 0;
                    for (int t = 0; t < currentWordTokens.Length; t++) {
                        if (autoCompleteTokens[t].StartsWith(currentWordTokens[t]))
                            tokenPos = t;
                        else {
                            valid = false;
                            break;
                        }
                    }

                    if (valid) {
                        if (!cAutoCompleteList.Contains(autoCompleteTokens[tokenPos]))
                            cAutoCompleteList.Add(autoCompleteTokens[tokenPos]);
                    }
                }

            }




            scintilla.AutoComplete.List.Clear();
            scintilla.AutoComplete.List.AddRange(cAutoCompleteList);

            this.currentWord = currentWord.ToString();
        }

        void scintilla_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) {
            if (e.Control == true && e.KeyCode == Keys.Space) {
                UpdateAutoCompleteList();
            }
        }

        string currentWord = null;
        private System.Collections.Generic.List<string> cAutoCompleteList = new System.Collections.Generic.List<string>();
        private System.Collections.Generic.List<string> autoCompleteList = new System.Collections.Generic.List<string>();
        private System.Collections.Generic.List<string> tmpAutoCompleteList = new System.Collections.Generic.List<string>();
        private System.Collections.Generic.List<string> tmp2AutoCompleteList = new System.Collections.Generic.List<string>();

        void scintilla_KeyUp(object sender, KeyEventArgs e) {
            if (scintilla.Selection.Start - 1 >= 0 && e.KeyData != Keys.Escape) {
                char cc = scintilla.Text[scintilla.Selection.Start - 1];
                switch (cc) {
                    case '.':
                    case ':':
                    case '>':
                        UpdateAutoCompleteList();
                        if (cAutoCompleteList.Count > 0 && !scintilla.AutoComplete.IsActive)
                            scintilla.AutoComplete.Show(); 
                        break;
                }
            }
        }

        private string correntWord = string.Empty;
        void scintilla_TextChanged(object sender, EventArgs e) {
            Console.WriteLine(scintilla.Selection.Start);
        }

		#endregion Constructors

		#region Misc Handlers

		private void DocumentForm_FormClosing(object sender, FormClosingEventArgs e)
		{
			if (Scintilla.Modified)
			{
				// Prompt if not saved
				string message = String.Format(
					CultureInfo.CurrentCulture,
					"The text in the {0} file has changed.{1}{2}Do you want to save the changes?",
					Text.TrimEnd(' ', '*'),
					Environment.NewLine,
					Environment.NewLine);

                DialogResult dr = MessageBox.Show(this, message, T3K.SketchEditor.Core.EditorManager.Title, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation);
				if (dr == DialogResult.Cancel)
				{
					// Stop closing
					e.Cancel = true;
					return;
				}
				else if (dr == DialogResult.Yes)
				{
					// Try to save before closing
					e.Cancel = !Save();
					return;
				}
			}

			// Close as normal
		}

		private void scintilla_ModifiedChanged(object sender, EventArgs e) {
			AddOrRemoveAsteric();
		}

		private void scintilla_StyleNeeded(object sender, StyleNeededEventArgs e) { }

		#endregion Misc Handlers

		#region Methods

		public bool Save()
		{
			if (String.IsNullOrEmpty(_filePath))
				return SaveAs();

			return Save(_filePath);
		}

		public bool SaveAs()
		{
			if (saveFileDialog.ShowDialog() == DialogResult.OK)
			{
				_filePath = saveFileDialog.FileName;
				return Save(_filePath);
			}
			
			return false;
		}

		public bool Save(string filePath)
		{
			using (FileStream fs = File.Create(filePath))
			using (BinaryWriter bw = new BinaryWriter(fs))
				bw.Write(scintilla.RawText, 0, scintilla.RawText.Length - 1); // Omit trailing NULL

			scintilla.Modified = false;
			return true;
		}

		private void AddOrRemoveAsteric()
		{
			if (scintilla.Modified)
			{
				if (!Text.EndsWith(" *"))
					Text += " *";
			}
			else
			{
				if (Text.EndsWith(" *"))
					Text = Text.Substring(0, Text.Length - 2);
			}
		}

		#endregion Methods
	}
}
